#pragma once

#include "Socket.hpp"
#include "log.hpp"

#include <pthread.h>
#include <vector>
#include <fstream>
#include <sstream>


static const int defaultport = 8080;

const std::string sep = "\r\n";
const std::string wwwroot = "./wwwroot";
const std::string homepage = "index.html";

class HttpServer;

class ThreadData
{
public:
    ThreadData(int sockfd, HttpServer* svr) 
    : sockfd_(sockfd), svr_(svr)
    {}

    int sockfd_;
    HttpServer* svr_;
};

class HttpRequest
{
public:
    HttpRequest()
    {}

    void Deserialize(std::string req)
    {
        // 分离请求行+报头和报文数据
        while(true)
        {
            size_t pos = req.find(sep);
            if(std::string::npos == pos) break;
            std::string tmp = req.substr(0,pos);
            if(tmp.empty()) break;
            req_header.push_back(tmp);
            req.erase(0,pos+sep.size());
        }
        text = req;
    }

    void Parse()
    {
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;
        file_path = wwwroot;
        if(url == "/" || url == "/index.html") {
            file_path += "/";
            file_path += homepage; // ./wwwroot/index.html
        }
        else file_path += url;
    }

    void DebugPrint()
    {
        for(auto &line : req_header)
        {
            std::cout << "--------------------------------" << std::endl;
            std::cout << line << "\n\n";
        }

        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }

    ~HttpRequest()
    {}
public:
    std::vector<std::string> req_header;
    std::string text;

    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path;
};

class HttpServer
{
public:
    HttpServer(uint16_t port=defaultport) : port_(port) 
    {}

    bool Start()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();

        lg(Info, "Server start successfully!");

        for(;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if(sockfd < 0)
            {
                lg(Warning, "accept fail!");
                continue;
            }
            lg(Info, "get a new link, sockfd: %d", sockfd);
            pthread_t tid;
            ThreadData* td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, Routine, td);
        }
    }

    static std::string ReadHtml(const string& path)
    {
        std::ifstream in(path, std::ios::binary);
        if(!in.is_open()) return "";

        std::string content;
        std::string line;
        while(std::getline(in, line))
        {
            content += line;
        }

        in.close();
        return content;
    }

    void HttpHandler(int sockfd)
    {
        char buffer[10240];
        int n = recv(sockfd, buffer, sizeof(buffer)-1, 0);
        if(n <= 0) return;

        buffer[n] = 0;
        std::cout << buffer << std::endl;
        HttpRequest req;
        req.Deserialize(buffer);
        req.Parse();
        // req.DebugPrint();

        std::string response_line;
        response_line = "HTTP/1.0 200 OK\r\n";

        std::string text = ReadHtml(req.file_path);
        if(text.empty())
        {
            lg(Info, "file not found.");
            close(sockfd);
            return;
        }

        std::string response_header = "Content-Length: ";
        response_header += std::to_string(text.size()); // Content-Length: 11
        response_header += "\r\n";

        //response_header += "Location: https://www.qq.com\r\n";
        std::string blank_line = "\r\n"; // \n

        std::string response = response_line;
        response += response_header;
        response += blank_line;
        response += text;

        send(sockfd, response.c_str(), response.size(), 0);

        close(sockfd);
    }

    static void* Routine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        td->svr_->HttpHandler(td->sockfd_);
        delete td;
        return nullptr;
    }

private:
    Sock listensock_;
    uint16_t port_;
};